/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ania2.draughtsboard;

import ania2.BoardPosition;
import ania2.enums.Fields;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.EnumMap;
import javax.imageio.ImageIO;
import org.jdesktop.application.Application;
import org.jdesktop.application.ResourceMap;

/**
 *
 * @author pawel
 */
public class DefaultBoardRenderer implements BoardRenderer, ComponentListener {

    private BufferedImage whitePieceImage;
    private BufferedImage blackPieceImage;
    private BufferedImage whiteQueenImage;
    private BufferedImage blackQueenImage;
    private BufferedImage boardImage;
    private Board board;
    private EnumMap<Fields, Point> fieldsMap;
    private int border;
    private int width;
    private int fieldWidth;
    private AffineTransform translation;
    private Area frame;
    private ResourceMap resourceMap;
    private Fields[] fields = Fields.values();

    public DefaultBoardRenderer(Board board) {
        resourceMap = Application.getInstance(ania2.Ania2App.class).getContext().getResourceMap(DefaultBoardRenderer.class);
        this.board = board;
        board.addComponentListener(this);
        loadImages();
        fieldsMap = new EnumMap<Fields, Point>(Fields.class);
        translation = new AffineTransform();
        initBoardParameters();
        initFieldsMap();
    }

    private void loadImages() {
        try {
            String directory = resourceMap.getResourcesDir();
            String whitePieceResourceName = directory + resourceMap.getString("whitePiece");
            URL whitePiecePath = resourceMap.getClassLoader().getResource(whitePieceResourceName);
            String blackPieceResourceName = directory + resourceMap.getString("blackPiece");
            URL blackPiecePath = resourceMap.getClassLoader().getResource(blackPieceResourceName);
            String whiteQueenResourceName = directory + resourceMap.getString("whiteQueen");
            URL whiteQueenPath = resourceMap.getClassLoader().getResource(whiteQueenResourceName);
            String blackQueenResourceName = directory + resourceMap.getString("blackQueen");
            URL blackQueenPath = resourceMap.getClassLoader().getResource(blackQueenResourceName);
            String boardResourceName = directory + resourceMap.getString("board");
            URL boardPath = resourceMap.getClassLoader().getResource(boardResourceName);

            whitePieceImage = ImageIO.read(whitePiecePath);
            blackPieceImage = ImageIO.read(blackPiecePath);
            whiteQueenImage = ImageIO.read(whiteQueenPath);
            blackQueenImage = ImageIO.read(blackQueenPath);
            boardImage = ImageIO.read(boardPath);
        } catch (IOException e) {
            System.err.println("Błąd odczytu plików");
        } catch (ResourceMap.LookupException e) {
            System.err.println("Błąd odczytu plików");
        } catch (IllegalArgumentException e) {
            System.err.println("Błąd odczytu plików");
        }
    }

    private void initFieldsMap() {
        String name = new String();
        int x, y;
        for (int i = 1; i <= 8; i++) {
            y = border + (8 - i) * fieldWidth;
            for (int j = 0; j <= 3; j++) {
                if (i % 2 != 0) {
                    name = String.valueOf(Character.toChars('A' + 2 * j)) + i;
                    x = border + 2 * j * fieldWidth;
                } else {
                    name = String.valueOf(Character.toChars('B' + 2 * j)) + i;
                    x = border + (2 * j + 1) * fieldWidth;
                }
                fieldsMap.put(Fields.valueOf(name), new Point(x, y));
            }
        }
    }

    private void initBoardParameters() {
        border = board.getBorderWidth();
        if ((board.getWidth() - 2 * border) % 8 != 0) {
            int newSize = board.getWidth() - (board.getWidth() - 2 * border) % 8;
            board.setSize(newSize, newSize);
        }
        width = board.getWidth() - 2 * border;
        fieldWidth = Math.round(width / 8);
        frame = getFrame();
    }

    private Area getFrame() {
        int frameWidth = board.getFieldFrameWidth();
        Area square1 = new Area(new Rectangle(fieldWidth, fieldWidth));
        Area square2 = new Area(new Rectangle(frameWidth, frameWidth,
                fieldWidth - 2 * frameWidth, fieldWidth - 2 * frameWidth));
        square1.subtract(square2);
        return square1;
    }

    public void paintBoard(Graphics g, Board board) {
        Graphics2D g2 = (Graphics2D) g;
        AffineTransform rotation = new AffineTransform();
        if (board.isRotated()) {
            rotation.rotate(Math.PI, board.getWidth() / 2, board.getWidth() / 2);
            g2.transform(rotation);
        }
        //wypełnienie ramki
        board.setBackground(Color.DARK_GRAY);
        //wypełnienie białych pól
        g2.setColor(board.getWhiteFieldColour());
        g2.fillRect(border, border, width, width);
        //wypełnienie czarnych pól
        g2.setColor(board.getBlackFieldColour());
        for (Fields f : fieldsMap.keySet()) {
            g2.fillRect(fieldsMap.get(f).x, fieldsMap.get(f).y, fieldWidth, fieldWidth);
        }

        //opis planszy na ramce
        if (border > 0 && board.isBoardDescriptionPrinted()) {
            g2.setColor(Color.WHITE);
            for (int i = 1; i <= 8; i++) {
                g2.drawString(String.valueOf(i), border / 3, border + (8 - i) * fieldWidth + 0.6f * fieldWidth);
                g2.drawString(String.valueOf(Character.toChars('a' + i - 1)), border + i * fieldWidth - 0.6f * fieldWidth, 1.6f * border + width);
            }
        }

        g2.drawImage(boardImage, border, border, width, width, board);

        //piony
            BoardPosition position = board.getPosition();
            long allWhite = position.getWhitePieces();
            long allBlack = position.getBlackPieces();
            long allQueens = position.getQueens();
            long whitePieces = allWhite & ~allQueens;
            long whiteQueens = allWhite & allQueens;
            long blackPieces = allBlack & ~allQueens;
            long blackQueens = allBlack & allQueens;

            long whitePiece = Long.highestOneBit(whitePieces);
            while (whitePiece != 0) {
                whitePieces ^= whitePiece;
                int index = 64 - Long.toBinaryString(whitePiece).length();
                Fields field = fields[index];
                g2.drawImage(whitePieceImage, fieldsMap.get(field).x, fieldsMap.get(field).y, fieldWidth, fieldWidth, board);
                whitePiece = Long.highestOneBit(whitePieces);
            }
            long whiteQueen = Long.highestOneBit(whiteQueens);
            while (whiteQueen != 0) {
                whiteQueens ^= whiteQueen;
                int index = 64 - Long.toBinaryString(whiteQueen).length();
                Fields field = fields[index];
                g2.drawImage(whiteQueenImage, fieldsMap.get(field).x, fieldsMap.get(field).y, fieldWidth, fieldWidth, board);
                whiteQueen = Long.highestOneBit(whiteQueens);
            }
            long blackPiece = Long.highestOneBit(blackPieces);
            while (blackPiece != 0) {
                blackPieces ^= blackPiece;
                int index = 64 - Long.toBinaryString(blackPiece).length();
                Fields field = fields[index];
                g2.drawImage(blackPieceImage, fieldsMap.get(field).x, fieldsMap.get(field).y, fieldWidth, fieldWidth, board);
                blackPiece = Long.highestOneBit(blackPieces);
            }
            long blackQueen = Long.highestOneBit(blackQueens);
            while (blackQueen != 0) {
                blackQueens ^= blackQueen;
                int index = 64 - Long.toBinaryString(blackQueen).length();
                Fields field = fields[index];
                g2.drawImage(blackQueenImage, fieldsMap.get(field).x, fieldsMap.get(field).y, fieldWidth, fieldWidth, board);
                blackQueen = Long.highestOneBit(blackQueens);
            }
        /*for (int i = 0; i < position.length; i++) {
        if (position[i] == 0) {
        continue;
        }
        row = (int) Math.floor(i / 4);
        column = (row % 2 == 0) ? 2 * (i % 8) : 2 * (i % 4) + 1;
        String name = String.valueOf(Character.toChars('A' + column)) + (row + 1);
        switch (position[i]) {
        case 4:
        g2.setColor(Color.BLACK);
        img = blackQueenImage;
        break;
        case 3:
        g2.setColor(Color.BLACK);
        img = blackPieceImage;
        break;
        case 1:
        g2.setColor(Color.WHITE);
        img = whitePieceImage;
        break;
        case 2:
        g2.setColor(Color.WHITE);
        img = whiteQueenImage;
        break;
        default:
        break;
        }
        if (img == null) {
        g2.fillOval(fieldsMap.get(FieldsEnum.valueOf(name)).x, fieldsMap.get(FieldsEnum.valueOf(name)).y, fieldWidth - 16, fieldWidth - 16);
        } else {
        g2.drawImage(img, fieldsMap.get(FieldsEnum.valueOf(name)).x, fieldsMap.get(FieldsEnum.valueOf(name)).y, fieldWidth, fieldWidth, board);
        }
        }*/

        //atrybuty
        try {
            for (Fields f : board.getFieldsWithHighlightedFrame()) {
                Point p = fieldsMap.get(f);
                g2.setColor(board.getFieldFrameColour());
                translation.setToIdentity();
                translation.translate(p.x, p.y);
                Area frame2 = frame.createTransformedArea(translation);
                g2.fill(frame2);
            }
        } catch (NullPointerException e) {
        }
    /*
    for (BoardAttribute a : board.getAttributes()) {
    switch (a.getAttribute()) {
    case FRAME:
    for (int fieldIndex : a.getFields()) {
    Point p = (Point) fieldsMap.values().toArray()[fieldIndex];
    g2.setColor(a.getColour());
    translation.setToIdentity();
    translation.translate(p.x, p.y);
    Area frame2 = frame.createTransformedArea(translation);
    g2.fill(frame2);
    }
    break;
    case HIGHLIGHTING_FIELD:
    for (int fieldIndex : a.getFields()) {
    Point p = (Point) fieldsMap.values().toArray()[fieldIndex];
    g2.setColor(a.getColour());
    g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, a.getAlpha()));
    translation.setToIdentity();
    translation.translate(p.x, p.y);
    g2.fillRect(p.x, p.y, fieldWidth, fieldWidth);
    }
    break;
    case ARROW:
    Point p1 = (Point) fieldsMap.values().toArray()[a.getFields().get(0)];
    Point p2 = (Point) fieldsMap.values().toArray()[a.getFields().get(a.getFields().size() - 1)];
    //kierunek strzałki: 1 jeśli od lewej z góry na dół, -1 jeśli odwrotnie
    int direction = (p2.x - p1.x) * (p2.y - p1.y) > 0 ? 1 : -1;
    g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, a.getAlpha()));
    g2.setStroke(new BasicStroke(arrowLineWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER));
    GradientPaint gradientPaint = new GradientPaint(p1.x, p1.y, Color.WHITE, p1.x + 4, p1.y - direction * 4, a.getColour(), true);
    g2.setPaint(gradientPaint);
    AffineTransform savedAT = g2.getTransform();
    g2.transform(arrowTranslation);
    //signum... do skrócenia linii, aby nie osiągała środka pola - miejsce na grot
    g2.drawLine(p1.x, p1.y, Math.round(p2.x - Math.signum(p2.x - p1.x) * arrowCurtailment),
    Math.round(p2.y - Math.signum(p2.y - p1.y) * arrowCurtailment));
    AffineTransform at = new AffineTransform();
    //odpowiednie skierowanie grotu strzałki
    at.rotate(-direction * (p2.x - p1.x), direction * (p2.y - p1.y));
    translation.setToIdentity();
    translation.translate(p2.x, p2.y);
    translation.concatenate(at);
    g2.transform(translation);
    GradientPaint gradientPaint2 = new GradientPaint(arrowTail.xpoints[2], arrowTail.ypoints[2], a.getColour(),
    (arrowTail.xpoints[2] + Math.abs(arrowTail.xpoints[2] - arrowTail.xpoints[1]) / 2),
    (arrowTail.ypoints[2] + Math.abs(arrowTail.ypoints[2] - arrowTail.ypoints[1]) / 2), Color.WHITE, true);
    g2.setPaint(gradientPaint2);
    g2.drawPolygon(arrowTail);
    g2.setTransform(savedAT);
    break;
    }
    }*/
    }

    public int getBorderWidth() {
        return border;
    }

    public int getFieldWidth() {
        return fieldWidth;
    }

    public void componentHidden(ComponentEvent e) {
        //throw new UnsupportedOperationException("Not supported yet.");
    }

    public void componentMoved(ComponentEvent e) {
        //throw new UnsupportedOperationException("Not supported yet.");
    }

    public void componentResized(ComponentEvent e) {
        //int size = Math.min(Math.min(board.getWidth(), board.getHeight()), 820);
        int size = Math.min(Math.min(board.getWidth(), board.getHeight()), board.getParent().getHeight());
        board.setSize(size, size);
        initBoardParameters();
        initFieldsMap();
    }

    public void componentShown(ComponentEvent e) {
        //throw new UnsupportedOperationException("Not supported yet.");
    }
}
